use std::fmt::Debug;
use std::future::Future;
use std::pin::Pin;
use std::sync::Arc;
use tokio::task::JoinHandle;
use async_trait::async_trait;

/// Dispatch trait for scheduling functions
#[async_trait]
pub trait Dispatcher: Send + Sync + Debug {
    /// Schedule a function to be executed
    fn schedule<F>(&self, f: F) -> JoinHandle<()>
    where
        F: FnOnce() -> Pin<Box<dyn Future<Output = ()> + Send + Unpin>> + Send + 'static;
        
    /// Get the throughput of the dispatcher
    fn throughput(&self) -> usize;
}

/// Object-safe trait for dispatchers
pub trait DispatcherTrait: Send + Sync + Debug {
    /// Schedule a function to be executed (object-safe version)
    fn schedule_boxed(
        &self,
        f: Box<dyn FnOnce() -> Pin<Box<dyn Future<Output = ()> + Send + Unpin>> + Send + 'static>,
    ) -> JoinHandle<()>;
    
    /// Get the throughput of the dispatcher
    fn throughput(&self) -> usize;
}

// Implement DispatcherTrait for all Dispatcher implementors
impl<T: Dispatcher + ?Sized> DispatcherTrait for T {
    fn schedule_boxed(
        &self,
        f: Box<dyn FnOnce() -> Pin<Box<dyn Future<Output = ()> + Send + Unpin>> + Send + 'static>,
    ) -> JoinHandle<()> {
        self.schedule(move || f())
    }
    
    fn throughput(&self) -> usize {
        self.throughput()
    }
}

/// Thread pool dispatcher implementation
#[derive(Debug)]
pub struct ThreadPoolDispatcher {
    throughput: usize,
}

impl ThreadPoolDispatcher {
    /// Create a new thread pool dispatcher
    pub fn new(throughput: usize) -> Self {
        Self { throughput }
    }
}

impl DispatcherTrait for ThreadPoolDispatcher {
    fn schedule_boxed(
        &self,
        f: Box<dyn FnOnce() -> Pin<Box<dyn Future<Output = ()> + Send + Unpin>> + Send + 'static>,
    ) -> JoinHandle<()> {
        tokio::spawn(async move {
            let future = f();
            future.await;
        })
    }
    
    fn throughput(&self) -> usize {
        self.throughput
    }
}

/// Synchronous dispatcher implementation
#[derive(Debug)]
pub struct SynchronousDispatcher {
    throughput: usize,
}

impl SynchronousDispatcher {
    /// Create a new synchronous dispatcher
    pub fn new(throughput: usize) -> Self {
        Self { throughput }
    }
}

impl DispatcherTrait for SynchronousDispatcher {
    fn schedule_boxed(
        &self,
        f: Box<dyn FnOnce() -> Pin<Box<dyn Future<Output = ()> + Send + Unpin>> + Send + 'static>,
    ) -> JoinHandle<()> {
        tokio::spawn(async move {
            let future = f();
            future.await;
        })
    }
    
    fn throughput(&self) -> usize {
        self.throughput
    }
}

/// Convenience methods for creating dispatchers
pub struct Dispatchers;

impl Dispatchers {
    /// Create a thread pool dispatcher
    pub fn thread_pool() -> Arc<dyn crate::dispatcher_enum::DispatcherTrait> {
        Arc::new(crate::dispatcher_enum::DispatcherEnum::thread_pool())
    }
    
    /// Create a synchronous dispatcher
    pub fn synchronous() -> Arc<dyn crate::dispatcher_enum::DispatcherTrait> {
        Arc::new(crate::dispatcher_enum::DispatcherEnum::synchronous())
    }
}

/// Adapter that wraps our DispatcherTrait for use with dispatcher_enum::DispatcherTrait
#[derive(Debug)]
pub struct DispatcherAdapter {
    inner: Arc<dyn DispatcherTrait>,
}

impl DispatcherAdapter {
    /// Create a new adapter
    pub fn new(inner: Arc<dyn DispatcherTrait>) -> Self {
        Self { inner }
    }
}

impl crate::dispatcher_enum::DispatcherTrait for DispatcherAdapter {
    fn schedule_boxed(
        &self,
        f: Box<dyn FnOnce() -> Pin<Box<dyn Future<Output = ()> + Send + Unpin>> + Send + 'static>,
    ) -> JoinHandle<()> {
        self.inner.schedule_boxed(f)
    }
    
    fn throughput(&self) -> usize {
        self.inner.throughput()
    }
} 